package org.convallaria.system.biz.service.auth.impl;

import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.framework.common.constants.CommonConstants;
import org.convallaria.framework.common.exception.BusinessException;
import org.convallaria.framework.common.exception.ExceptionConstants;
import org.convallaria.framework.security.core.JwtTokenProvider;
import org.convallaria.framework.security.core.SecurityUser;
import org.convallaria.framework.security.service.TokenService;
import org.convallaria.framework.security.util.SecurityUtils;
import org.convallaria.system.biz.entity.dto.LoginDTO;
import org.convallaria.system.biz.entity.dto.TokenDTO;
import org.convallaria.system.biz.entity.SysUser;
import org.convallaria.system.biz.entity.SysLoginLog;
import org.convallaria.system.biz.service.auth.AuthService;
import org.convallaria.system.biz.service.impl.PasswordPolicyServiceImpl;
import org.convallaria.system.biz.service.sys.SysUserService;
import org.convallaria.system.biz.service.sys.SysRoleService;
import org.convallaria.system.biz.service.sys.SysMenuService;
import org.convallaria.system.biz.service.sys.SysDeptService;
import org.convallaria.system.biz.service.sys.SysPostService;
import org.convallaria.system.biz.service.auth.CaptchaService;
import org.convallaria.system.biz.service.LoginAttemptService;
import org.convallaria.system.biz.service.PasswordPolicyService;
import org.convallaria.system.biz.service.SecurityAuditService;
import org.convallaria.system.biz.service.TokenBlacklistService;
import org.convallaria.system.biz.service.SecurityConfigService;
import org.convallaria.system.biz.service.UserOnlineService;
import org.convallaria.system.biz.service.sys.SysLoginLogService;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import io.jsonwebtoken.Claims;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 认证服务实现
 * 
 * 功能特性：
 * - 基于新数据结构的用户认证
 * - 多租户支持
 * - 登录日志记录
 * - Token管理
 * - 权限验证
 * - 安全控制
 * 
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final JwtTokenProvider jwtTokenProvider;
    private final TokenService tokenService;
    private final SysUserService sysUserService;
    private final SysRoleService sysRoleService;
    private final SysMenuService sysMenuService;
    private final SysDeptService sysDeptService;
    private final SysPostService sysPostService;
    private final CaptchaService captchaService;
    private final LoginAttemptService loginAttemptService;
    private final PasswordPolicyService passwordPolicyService;
    private final SecurityAuditService securityAuditService;
    private final TokenBlacklistService tokenBlacklistService;
    private final SecurityConfigService securityConfigService;
    private final UserOnlineService userOnlineService;
    private final SysLoginLogService sysLoginLogService;
    private final PasswordEncoder passwordEncoder;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TokenDTO login(LoginDTO loginDTO) {
        // 获取客户端IP
        String loginIp = getClientIp();
        // 获取登录位置
        String loginLocation = getLoginLocation(loginIp);
        // 获取浏览器信息
        String browser = getBrowser();
        // 获取操作系统信息
        String os = getOs();

        // 默认成功
        String status = "0";
        String msg = "登录成功";
        // 默认租户ID
        Long tenantId = 1L;

        try {
            // 1. 检查账户是否被锁定
            if (loginAttemptService.isAccountLocked(loginDTO.getUsername(), loginIp)) {
                status = "1";
                msg = "账户已被锁定，请稍后再试";
                sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, tenantId);
                throw new BusinessException(CommonConstants.FORBIDDEN, "账户已被锁定，请稍后再试");
            }

            // 2. 验证码校验（如果启用）
            if (CharSequenceUtil.isNotBlank(loginDTO.getCaptcha())) {
                if (!captchaService.validateCaptcha(loginDTO.getCaptchaKey(), loginDTO.getCaptcha())) {
                    status = "1";
                    msg = "验证码错误或已过期";
                    sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, tenantId);
                    throw new BusinessException(CommonConstants.BAD_REQUEST, "验证码错误或已过期");
                }
            }

            // 3. 查询用户信息
            SysUser user = sysUserService.selectByUsername(loginDTO.getUsername());
            if (user == null) {
                status = "1";
                msg = "用户不存在";
                loginAttemptService.recordFailedAttempt(loginDTO.getUsername(), loginIp);
                sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, tenantId);
                throw new BusinessException(CommonConstants.BAD_REQUEST, ExceptionConstants.USER_NOT_FOUND);
            }

            // 4. 密码验证
            if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) {
                status = "1";
                msg = "密码错误";
                loginAttemptService.recordFailedAttempt(loginDTO.getUsername(), loginIp);
                sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, user.getTenantId());
                throw new BusinessException(CommonConstants.BAD_REQUEST, ExceptionConstants.PASSWORD_ERROR);
            }

            // 5. 状态检查
            if (!"0".equals(user.getStatus())) {
                status = "1";
                msg = "用户已被禁用";
                sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, user.getTenantId());
                throw new BusinessException(CommonConstants.FORBIDDEN, ExceptionConstants.USER_DISABLED);
            }

            // 6. 获取用户角色和权限
            Set<String> roles = sysUserService.getUserRoles(user.getUserId());
            Set<String> permissions = sysUserService.getUserPermissions(user.getUserId());

            // 7. 清除登录失败记录
            loginAttemptService.clearFailedAttempts(loginDTO.getUsername(), loginIp);

            // 8. 构建SecurityUser
            SecurityUser securityUser = SecurityUser.builder()
                    .userId(user.getUserId())
                    .username(user.getUserName())
                    .password(user.getPassword())
                    .tenantId(user.getTenantId())
                    .deptId(user.getDeptId())
                    .status(Integer.parseInt(user.getStatus()))
                    .roles(roles)
                    .permissions(permissions)
                    .build();

            // 9. 生成Token
            String accessToken = jwtTokenProvider.createToken(securityUser);
            String refreshToken = tokenService.createRefreshToken(user.getUserId());

            // 10. 存储Token
            tokenService.storeToken(accessToken, securityUser);

            // 11. 更新用户最后登录信息
            sysUserService.updateLastLoginInfo(user.getUserId(), loginIp);

            // 12. 记录登录日志
            sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, user.getTenantId());

            // 13. 记录安全审计日志
            securityAuditService.recordLoginEvent(user.getUserId(), user.getUserName(), loginIp, true, null);

            // 14. 设置用户在线状态
            userOnlineService.setUserOnline(user.getUserId(), accessToken, loginIp, browser, LocalDateTime.now());

            // 15. 构建返回结果
            return TokenDTO.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .tokenType("Bearer")
                    .expiresIn(jwtTokenProvider.getTokenExpireTime(accessToken))
                    .userInfo(buildUserInfo(user, roles, permissions))
                    .build();
                    
        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("登录异常", e);
            status = "1";
            msg = "登录异常: " + e.getMessage();
            sysLoginLogService.recordLoginLog(loginDTO.getUsername(), loginIp, loginLocation, browser, os, status, msg, tenantId);
            securityAuditService.recordLoginEvent(null, loginDTO.getUsername(), loginIp, false, e.getMessage());
            throw new BusinessException(CommonConstants.ERROR, ExceptionConstants.LOGIN_FAILED, e);
        }
    }

    @Override
    public void logout(String token) {
        if (StrUtil.isBlank(token)) {
            return;
        }

        try {
            // 1. 验证Token
            if (!jwtTokenProvider.validateToken(token)) {
                return;
            }

            // 2. 从Token中获取用户信息
            Claims claims = jwtTokenProvider.parseToken(token);
            if (claims == null) {
                return;
            }

            Long userId = Long.valueOf(claims.get("userId", String.class));
            String username = claims.get("username", String.class);
            Long tenantId = Long.valueOf(claims.get("tenantId", String.class));

            // 3. 将Token加入黑名单
            tokenBlacklistService.addToBlacklist(token, TokenBlacklistService.BlacklistReason.LOGOUT.getDescription());

            // 4. 删除Token
            tokenService.removeToken(token);

            // 5. 记录登出日志
            log.info("用户 {} 登出成功", username);

            // 6. 记录安全审计日志
            securityAuditService.recordLoginEvent(userId, username, getClientIp(), false, "用户登出");

        } catch (Exception e) {
            log.error("登出失败", e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TokenDTO refreshToken(String refreshToken) {
        try {
            // 1. 验证刷新token
            Long userId = tokenService.validateRefreshToken(refreshToken);
            if (userId == null) {
                throw new BadCredentialsException("刷新token无效或已过期");
            }

            // 2. 获取用户信息
            SysUser user = sysUserService.getUserById(userId);
            if (user == null || !"0".equals(user.getStatus())) {
                throw new BadCredentialsException("用户不存在或已被禁用");
            }

            // 3. 获取角色和权限
            Set<String> roles = sysUserService.getUserRoles(userId);
            Set<String> permissions = sysUserService.getUserPermissions(userId);

        // 4. 构建SecurityUser
        SecurityUser securityUser = SecurityUser.builder()
                .userId(user.getUserId())
                .username(user.getUserName())
                .tenantId(user.getTenantId())
                .deptId(user.getDeptId())
                .status(Integer.parseInt(user.getStatus()))
                .roles(roles)
                .permissions(permissions)
                .build();

            // 5. 生成新的Token
            String newAccessToken = jwtTokenProvider.createToken(securityUser);
            String newRefreshToken = tokenService.createRefreshToken(userId);

            // 6. 存储新Token
            tokenService.storeToken(newAccessToken, securityUser);

            // 7. 删除旧的刷新token
            tokenService.removeRefreshToken(refreshToken);

            // 8. 返回结果
            return TokenDTO.builder()
                    .accessToken(newAccessToken)
                    .refreshToken(newRefreshToken)
                    .tokenType("Bearer")
                    .expiresIn(jwtTokenProvider.getTokenExpireTime(newAccessToken))
                    .userInfo(buildUserInfo(user, roles, permissions))
                    .build();
        } catch (Exception e) {
            log.error("刷新Token异常", e);
            throw new BusinessException(CommonConstants.ERROR, "刷新Token失败", e);
        }
    }

    @Override
    public Object getCurrentUserInfo() {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            return null;
        }

        SysUser user = sysUserService.getUserById(currentUser.getUserId());
        if (user == null) {
            return null;
        }

        return buildUserInfo(user, currentUser.getRoles(), currentUser.getPermissions());
    }

    @Override
    public Map<String, Object> getCurrentUserDetailInfo() {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            return null;
        }

        SysUser user = sysUserService.getUserById(currentUser.getUserId());
        if (user == null) {
            return null;
        }

        Map<String, Object> result = new HashMap<>();
        result.put("user", buildUserInfo(user, currentUser.getRoles(), currentUser.getPermissions()));
        result.put("dept", sysUserService.getUserDeptInfo(user.getUserId()));
        result.put("posts", sysUserService.getUserPostInfo(user.getUserId()));
        result.put("menus", getCurrentUserMenus());
        result.put("permissions", getCurrentUserPermissions());

        return result;
    }

    @Override
    public Object getCurrentUserMenus() {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            return null;
        }

        return sysMenuService.getUserMenuTree(currentUser.getUserId(), currentUser.getTenantId());
    }

    @Override
    public Object getCurrentUserPermissions() {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            return null;
        }

        return currentUser.getPermissions();
    }

    @Override
    public boolean validateToken(String token) {
        try {
            return jwtTokenProvider.validateToken(token);
        } catch (Exception e) {
            log.error("Token验证失败", e);
            return false;
        }
    }

    @Override
    public Object getUserInfoFromToken(String token) {
        try {
            Claims claims = jwtTokenProvider.parseToken(token);
            if (claims == null) {
                return null;
            }

            Long userId = Long.valueOf(claims.get("userId", String.class));
            SysUser user = sysUserService.getUserById(userId);
            if (user == null) {
                return null;
            }

            @SuppressWarnings("unchecked")
            Set<String> roles = (Set<String>) claims.get("roles");
            @SuppressWarnings("unchecked")
            Set<String> permissions = (Set<String>) claims.get("permissions");

            return buildUserInfo(user, roles, permissions);
        } catch (Exception e) {
            log.error("从Token获取用户信息失败", e);
            return null;
        }
    }


    @Override
    public boolean isUserOnline(Long userId) {
        return userOnlineService.isUserOnline(userId);
    }

    @Override
    public boolean forceLogout(Long userId) {
        return userOnlineService.forceUserOffline(userId, "管理员强制下线");
    }

    @Override
    public Object getOnlineUsers() {
        return userOnlineService.getOnlineUsers(1, 100);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean changePassword(String oldPassword, String newPassword) {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            throw new BusinessException(CommonConstants.FORBIDDEN, "用户未登录");
        }

        SysUser user = sysUserService.getUserById(currentUser.getUserId());
        if (user == null) {
            throw new BusinessException(CommonConstants.BAD_REQUEST, "用户不存在");
        }

        // 验证旧密码
        if (!passwordEncoder.matches(oldPassword, user.getPassword())) {
            securityAuditService.recordPasswordChangeEvent(user.getUserId(), null, false, "原密码错误");
            throw new BusinessException(CommonConstants.BAD_REQUEST, "原密码错误");
        }

        // 验证新密码策略
        PasswordPolicyService.PasswordValidationResult validationResult = 
            passwordPolicyService.validatePassword(newPassword, user.getUserId());
        if (!validationResult.isValid()) {
            securityAuditService.recordPasswordChangeEvent(user.getUserId(), null, false, validationResult.getMessage());
            throw new BusinessException(CommonConstants.BAD_REQUEST, validationResult.getMessage());
        }

        // 更新密码
        user.setPassword(passwordEncoder.encode(newPassword));
        boolean result = sysUserService.updateUser(user);

        if (result) {
            // 记录密码历史
            ((PasswordPolicyServiceImpl) passwordPolicyService).recordPasswordHistory(newPassword, user.getUserId());
            
            // 记录安全审计日志
            securityAuditService.recordPasswordChangeEvent(user.getUserId(), null, true, "密码修改成功");
        }

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean resetPassword(Long userId, String newPassword) {
        SysUser user = sysUserService.getUserById(userId);
        if (user == null) {
            throw new BusinessException(CommonConstants.BAD_REQUEST, "用户不存在");
        }

        // 更新密码
        user.setPassword(passwordEncoder.encode(newPassword));
        return sysUserService.updateUser(user);
    }

    @Override
    public boolean hasPermission(String permission) {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            return false;
        }

        return currentUser.getPermissions().contains(permission);
    }

    @Override
    public boolean hasRole(String role) {
        SecurityUser currentUser = SecurityUtils.getCurrentUser();
        if (currentUser == null) {
            return false;
        }

        return currentUser.getRoles().contains(role);
    }

    @Override
    public Object getUserLoginHistory(Long userId, Integer pageNum, Integer pageSize) {
        try {
            // 通过业务层查询用户登录历史
            com.baomidou.mybatisplus.core.metadata.IPage<SysLoginLog> result = 
                sysLoginLogService.getUserLoginHistory(userId, pageNum, pageSize);
            
            Map<String, Object> response = new HashMap<>();
            response.put("records", result.getRecords());
            response.put("total", result.getTotal());
            response.put("current", result.getCurrent());
            response.put("size", result.getSize());
            response.put("pages", result.getPages());
            
            return response;
        } catch (Exception e) {
            log.error("获取用户登录历史失败: userId={}", userId, e);
            return new HashMap<>();
        }
    }

    /**
     * 构建用户信息DTO
     */
    private TokenDTO.UserInfoDTO buildUserInfo(SysUser user, Set<String> roles, Set<String> permissions) {
        return TokenDTO.UserInfoDTO.builder()
                .userId(user.getUserId())
                .username(user.getUserName())
                .nickname(user.getNickName())
                .avatar(user.getAvatar())
                .email(user.getEmail())
                .mobile(user.getPhoneNumber())
                .deptId(user.getDeptId())
                .deptName(user.getDeptName() != null ? user.getDeptName() : "")
                .roles(roles)
                .permissions(permissions)
                .build();
    }

    /**
     * 获取客户端IP
     */
    private String getClientIp() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return "127.0.0.1";
            }
            
            HttpServletRequest request = attributes.getRequest();
            String ip = request.getHeader("X-Forwarded-For");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            
            // 处理多个IP的情况，取第一个
            if (ip != null && ip.contains(",")) {
                ip = ip.split(",")[0].trim();
            }
            
            return ip != null ? ip : "127.0.0.1";
        } catch (Exception e) {
            log.error("获取客户端IP失败", e);
            return "127.0.0.1";
        }
    }

    /**
     * 获取登录地点
     */
    private String getLoginLocation(String ip) {
        try {
            // 这里可以集成IP地理位置服务，如高德地图API、百度地图API等
            // 目前返回基于IP的简单判断
            if (ip == null || "127.0.0.1".equals(ip) || "localhost".equals(ip)) {
                return "本地";
            }
            
            // 简单的IP段判断（实际项目中应该使用专业的地理位置服务）
            if (ip.startsWith("192.168.") || ip.startsWith("10.") || ip.startsWith("172.")) {
                return "内网";
            }
            
            // 这里可以调用第三方API获取真实地理位置
            // 例如：return ipLocationService.getLocationByIp(ip);
            
            return "未知";
        } catch (Exception e) {
            log.error("获取登录地点失败: ip={}", ip, e);
            return "未知";
        }
    }

    /**
     * 获取浏览器信息
     */
    private String getBrowser() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return "未知";
            }
            
            HttpServletRequest request = attributes.getRequest();
            String userAgent = request.getHeader("User-Agent");
            
            if (userAgent == null || userAgent.isEmpty()) {
                return "未知";
            }
            
            userAgent = userAgent.toLowerCase();
            
            if (userAgent.contains("chrome") && !userAgent.contains("edg")) {
                return "Chrome";
            } else if (userAgent.contains("firefox")) {
                return "Firefox";
            } else if (userAgent.contains("safari") && !userAgent.contains("chrome")) {
                return "Safari";
            } else if (userAgent.contains("edg")) {
                return "Edge";
            } else if (userAgent.contains("opera")) {
                return "Opera";
            } else if (userAgent.contains("msie") || userAgent.contains("trident")) {
                return "Internet Explorer";
            } else if (userAgent.contains("postman")) {
                return "Postman";
            } else if (userAgent.contains("curl")) {
                return "curl";
            } else if (userAgent.contains("wget")) {
                return "wget";
            }
            
            return "未知";
        } catch (Exception e) {
            log.error("获取浏览器信息失败", e);
            return "未知";
        }
    }

    /**
     * 获取操作系统信息
     */
    private String getOs() {
        try {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                return "未知";
            }
            
            HttpServletRequest request = attributes.getRequest();
            String userAgent = request.getHeader("User-Agent");
            
            if (userAgent == null || userAgent.isEmpty()) {
                return "未知";
            }
            
            userAgent = userAgent.toLowerCase();
            
            if (userAgent.contains("windows nt 10.0")) {
                return "Windows 10";
            } else if (userAgent.contains("windows nt 6.3")) {
                return "Windows 8.1";
            } else if (userAgent.contains("windows nt 6.2")) {
                return "Windows 8";
            } else if (userAgent.contains("windows nt 6.1")) {
                return "Windows 7";
            } else if (userAgent.contains("windows nt 6.0")) {
                return "Windows Vista";
            } else if (userAgent.contains("windows nt 5.1")) {
                return "Windows XP";
            } else if (userAgent.contains("windows nt 5.0")) {
                return "Windows 2000";
            } else if (userAgent.contains("windows")) {
                return "Windows";
            } else if (userAgent.contains("mac os x")) {
                return "macOS";
            } else if (userAgent.contains("mac os")) {
                return "macOS";
            } else if (userAgent.contains("linux")) {
                return "Linux";
            } else if (userAgent.contains("android")) {
                return "Android";
            } else if (userAgent.contains("iphone") || userAgent.contains("ipad")) {
                return "iOS";
            } else if (userAgent.contains("unix")) {
                return "Unix";
            }
            
            return "未知";
        } catch (Exception e) {
            log.error("获取操作系统信息失败", e);
            return "未知";
        }
    }
}